<template>
  <div 
    ref="containerRef" />
</template>

<script setup lang="ts">
import { AmbientLight, GridHelper, Object3D, PCFSoftShadowMap, PerspectiveCamera, PointLight, Raycaster, Scene, Vector2, WebGLRenderer } from 'three';
import { OrbitControls } from 'three/examples/jsm/Addons.js';
import { GLTFLoader, type GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { onMounted, ref } from 'vue';

const containerRef = ref<HTMLElement>()
const rotate = true
const init = async () => {
  //场景
  const scene = new Scene()
  //相机
  const camera = new PerspectiveCamera(
    60,
    800 / 600,
    .1,
    1000
  )
  camera.position.set(2, 1.3, 3.5)
  //渲染器
  const render = new WebGLRenderer({
    antialias: true,
  })
  render.setSize(800, 600)
  render.setClearColor('#555')
  render.shadowMap.enabled = true
  render.shadowMap.type = PCFSoftShadowMap
  containerRef.value?.appendChild(
    render.domElement
  )
  //加载模型
  const loader = new GLTFLoader();
  const file: GLTF = await new Promise((res, rej) => {
    loader.load(
      '/static/model/Tesla/scene.gltf',
      gltf => res(gltf),
      ({ loaded, total }) =>
        console.log((loaded / total * 100) + '% loaded'),
      err => rej(err)
    )
  })
  //标记材质
  const objects: Object3D[] = []
  file.scene.traverse(child => {
    if (child.type == 'Mesh')
      objects.push(child)
    //console.log(child.name)
  })
  scene.add(file.scene)

  //加载地面
  const grid = new GridHelper(24, 24);
  grid.rotation.y = Math.PI / 2;
  scene.add(grid);

  /** 光源 */
  const ambientLight = new AmbientLight(0xffffff, 1);
  ambientLight.intensity = 4
  scene.add(ambientLight);
  const pointLight = new PointLight(0xffffff, 1);
  pointLight.position.set(5, 5, 0);
  pointLight.intensity = 600
  scene.add(pointLight);

  //控制器
  const controls = new OrbitControls(camera, render.domElement)
  controls.maxPolarAngle = 0.9 * Math.PI / 2
  controls.enableZoom = true
  controls.enableDamping = true
  let isInteracting = false;
  if(rotate){
    controls.addEventListener('start', () => isInteracting = true);
    controls.addEventListener('end', () => isInteracting = false);
    controls.addEventListener('change', () => {         
      angle = Math.atan2(
        camera.position.x, 
        camera.position.z
      );
    });
  }
  let angle = .3;
  const speed = 0.002;  // 旋转速度*/
  const animate = () => {
    requestAnimationFrame(animate);
    // 控制器
    controls.update()
    // 处理旋转
    if(!isInteracting && rotate){
      angle += speed;
      const currentRadius = Math.sqrt(
          camera.position.x * camera.position.x + 
          camera.position.z * camera.position.z
      );
      camera.position.set(
          currentRadius * Math.sin(angle),
          camera.position.y,
          currentRadius * Math.cos(angle)
      );
    }
    render.render(scene, camera);
  }
  animate();

  //处理事件
  const raycaster = new Raycaster()
  const mouse = new Vector2()
  const handleClick = (event: MouseEvent) => {
    mouse.x = (event.clientX / render.domElement.clientWidth) * 2 - 1;
    mouse.y = -(event.clientY / render.domElement.clientHeight) * 2 + 1;
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(objects);
    if (intersects.length <= 0)
      return
    interface InteractiveMesh extends Object3D {
      material: {
        color?: any
      };
    }
    const obj = intersects[0].object as InteractiveMesh
    obj.material.color.set(
      `#${Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, '0')}`
    )
  }
  render.domElement.addEventListener('click', handleClick)
}
onMounted(
  () => init()
)
</script>

<style scoped></style>